#include "qemu/osdep.h"
#include "hw/qdev-properties.h"
#include "sysemu/runstate.h"
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "qemu/qemu-print.h"
#include "crypto/random.h"
#include "qapi/error.h"

#define HP_SUBSYS_REGION_SIZE 0x1000
#define OFFSET_SHIFT 2

#define TYPE_HP_SUBSYS "hp_subsys"
#define HP_SUBSYS(obj) \
    OBJECT_CHECK(hp_subsys_state, (obj), TYPE_HP_SUBSYS)

typedef struct {
    SysBusDevice parent_obj;
    MemoryRegion iomem;
    uint32_t reg[HP_SUBSYS_REGION_SIZE / sizeof(uint32_t)];
} hp_subsys_state;

static const VMStateDescription vmstate_hp_subsys = {
    .name = TYPE_HP_SUBSYS,
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32_ARRAY(reg, hp_subsys_state, HP_SUBSYS_REGION_SIZE / sizeof(uint32_t)),
        VMSTATE_END_OF_LIST()
    }
};

static void hp_subsys_reset(DeviceState *d)
{
}

static uint64_t hp_subsys_read(void *opaque, hwaddr offset, unsigned size)
{
    hp_subsys_state *s = (hp_subsys_state *)opaque;
    switch (offset) {
        case 0x170 ... 0x174:
            s->reg[offset >> OFFSET_SHIFT] |= 0x39;
            return s->reg[offset >> OFFSET_SHIFT];
        default:
            break;
    }

    return 0;
}

static void hp_subsys_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
{
    hp_subsys_state *s = (hp_subsys_state *)opaque;
    
    const uint32_t reg_index = (uint32_t)(offset >> 2);
    s->reg[reg_index] = (uint32_t)val;
}

static const MemoryRegionOps hp_subsys_ops = {
    .read = hp_subsys_read,
    .write = hp_subsys_write,
    .endianness = DEVICE_LITTLE_ENDIAN,
    .valid.min_access_size = 4,
    .valid.max_access_size = 4,
    .impl.max_access_size = 4,
};

static void hp_subsys_init(Object *obj)
{
    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
    hp_subsys_state *s = HP_SUBSYS(obj);

    memory_region_init_io(&s->iomem, obj, &hp_subsys_ops, s, "hp_subsys", HP_SUBSYS_REGION_SIZE);
    sysbus_init_mmio(sd, &s->iomem);
}

static void hp_subsys_realize(DeviceState *d, Error **errp)
{
}

static void hp_subsys_finalize(Object *obj)
{
}

static Property hp_subsys_properties[] = {
    DEFINE_PROP_END_OF_LIST(),
};

static void hp_subsys_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);

    dc->realize = hp_subsys_realize;
    dc->reset = hp_subsys_reset;
    dc->vmsd = &vmstate_hp_subsys;
    device_class_set_props(dc, hp_subsys_properties);
}

static const TypeInfo hp_subsys_info = {
    .name          = TYPE_HP_SUBSYS,
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(hp_subsys_state),
    .instance_init = hp_subsys_init,
    .instance_finalize = hp_subsys_finalize,
    .class_init    = hp_subsys_class_init,
};

static void hp_subsys_register_types(void)
{
    type_register_static(&hp_subsys_info);
}

type_init(hp_subsys_register_types)
